package com.example.pagoda;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Enigma 破解程序
 * 思路，如：https://docs.qq.com/doc/DTEVwcElyaUVBckJT 初始位置为图一时
 * 敲击A ,A初始位置1，各个转子 的内部偏移为 deviationA = 24 deviationB = 19 deviationC = 10
 * 得 1+24+19+10 = 54 ，54%26=2 ，输出B
 * 解密： 54-10-19-24=1
 * 题目为初始位置不确定的情况，所以爆破所有可能的初始位置，并且通过PAGODA来排除其他可能得到明文
 * Created by tangz on 2019/10/2.
 */
public class Decrypt {

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        //0位置补0，从1开始【因为转的第一列数字都是有序的，所以只需要用三个数组。转子旋转，不需要把数组元素挨个位移（时间复杂度x26n）。只需要用一个标记变量实现】
        int rotorA[] = new int[]{0, 1, 19, 10, 14, 26, 20, 8, 16, 7, 22, 4, 11, 5, 17, 9, 12, 23, 18, 2, 25, 6, 24, 13, 21, 3, 15};
        int rotorB[] = new int[]{0, 1, 6, 4, 15, 3, 14, 12, 23, 5, 16, 2, 22, 19, 11, 18, 25, 24, 13, 7, 10, 8, 21, 9, 26, 17, 20};
        int rotorC[] = new int[]{0, 8, 18, 26, 17, 20, 22, 10, 3, 13, 11, 4, 23, 5, 24, 9, 12, 25, 16, 19, 6, 15, 21, 2, 7, 1, 14};
        Map<Integer, Integer> rotorAMap = new HashMap<Integer, Integer>();
        Map<Integer, Integer> rotorBMap = new HashMap<Integer, Integer>();
        Map<Integer, Integer> rotorCMap = new HashMap<Integer, Integer>();
        for (int i = 1; i <= 26; i++) {
            rotorAMap.put(rotorA[i], i);
            rotorBMap.put(rotorB[i], i);
            rotorCMap.put(rotorC[i], i);
        }
        //计算转子的内部字母对的偏移大小
        int deviationA[] = new int[27];
        int deviationB[] = new int[27];
        int deviationC[] = new int[27];
        for (int i = 1; i <= 26; i++) {
            int t1 = (rotorAMap.get(i) - i + 26) % 26;
            int t2 = (rotorBMap.get(i) - i + 26) % 26;
            int t3 = (rotorCMap.get(i) - i + 26) % 26;
            deviationA[i] = t1 == 0 ? 26 : t1;
            deviationB[i] = t2 == 0 ? 26 : t2;
            deviationC[i] = t3 == 0 ? 26 : t3;
        }
        //明文
        String plainStr = "PAGODA";
        int plainText[] = stringToInts(plainStr);
        //密文 RAXLZSDKQAGECXWGVK
        String cipherStr = "RAXLZSDKQAGECXWGVK";
        int cipherText[] = stringToInts(cipherStr);
        //转子初始偏移位置
        int dA = 0, dB = 0, dC = 0;
        //储存结果，可能多个
        List<String> results = new ArrayList<String>();
        for (int i = 0; i < 26; i++) {
            for (int j = 0; j < 26; j++) {
                for (int k = 0; k < 26; k++) {
                    dA = 0 - i;
                    dB = 0 - j;
                    dC = 0 - k;
                    //字母A对应转子初始位置 对应顺序下标位置
                    int cA, cB, cC;
                    StringBuffer stringBuffer = new StringBuffer();
                    //已知明文长度
                    int plength = 6;
                    for (int p = 0; p < cipherText.length; p++) {
                        cC = cipherText[p];
                        //t1,t2,t3为实际对应转子的 位置。deviation为需要加减的偏移量
                        int t1 = ((cC + dC + 26) % 26);
                        t1 = t1 == 0 ? 26 : t1;
                        cB = (cC - deviationC[rotorC[t1]] + 26) % 26;
                        int t2 = ((cB + dB + 26) % 26);
                        t2 = t2 == 0 ? 26 : t2;
                        cA = (cB - deviationB[rotorB[t2]] + 26) % 26;
                        int t3 = ((cA + dA + 26) % 26);
                        t3 = t3 == 0 ? 26 : t3;
                        //逆向推理明文
                        int plain = ((cipherText[p] + 26*3) - deviationA[rotorA[t3]] - deviationB[rotorB[t2]] - deviationC[rotorC[t1]]) % 26;
                        stringBuffer.append((char) (plain + 64));
                        if (plength > 0 && plain == plainText[p]) {
                            plength--;
                        }
                        if ((p < 5 && plain != plainText[p]) || (p == 5 && plength > 0)) {
                            break;
                        }
                        //dC 旋转
                        dC--;
                    }
                    if (stringBuffer.toString().length() == 18) {
                        results.add(stringBuffer.toString());
                    }
                }
            }
        }
        long end = System.currentTimeMillis();
        System.out.println("可能的明文：" + results + "\n耗时：" + (end - start) + "ms");
    }

    private static int[] stringToInts(String plainStr) {
        char plainChars[] = plainStr.toCharArray();
        int plainText[] = new int[plainChars.length];
        for (int i = 0; i < plainChars.length; i++) {
            plainText[i] = plainChars[i] - 'A' + 1;
        }
        return plainText;
    }

}